audit library

The SEO auditor — pure Dart, no Flutter, no dart:io.

It answers one question: is this site actually SEO-correct? — and it answers it from the route table, before anything is built. The package owns that table, so it already knows every URL, every title and every node the server will render. A broken internal link is an href that matchSeoRoute cannot match; nothing has to be crawled and no HTML has to be parsed.

What it catches is the class of mistake the renderer cannot catch, because each one is a perfectly legal use of the API:

  • a robots: 'noindex' page that is still listed in sitemap.xml,
  • a concrete route shadowed by a :param pattern declared before it, so the page never runs while the sitemap still advertises its URL,
  • a canonicalUrl the URL policy refuses — which leaves the page with no canonical, and suppresses the automatic one as well,
  • a schema value that JSON cannot encode, which throws when the page renders rather than when the table is written.

Put it in a test and it runs in the CI you already have:

test('the site is SEO-healthy', () async {
  assertSeoHealthy(
    await auditSeoRoutes(routes: seoRoutes, siteBase: siteBase),
  );
});

Use assertSeoHealthy rather than writing the comparison by hand. An earlier version of this comment suggested expect(report.describe(), isNot(contains('[error]'))), which never failed — describe() marks an error with x, not with [error] — so the recommended test passed on a broken site.

Or run it from a script, the same way prerenderSite is run:

// bin/seo_audit.dart
final report = await auditSeoRoutes(routes: seoRoutes, siteBase: siteBase);
stdout.write(report.describe());
exit(report.passes() ? 0 : 1);

Classes

SeoAuditPolicy
What the audit considers a problem.
SeoAuditReport
The result of one audit run.
SeoFinding
One problem, on one page.

Enums

SeoSeverity
How much a finding matters.

Extension Types

SeoCheck
The identity of a check, e.g. title.missing.

Functions

assertSeoHealthy(SeoAuditReport report, {SeoSeverity threshold = SeoSeverity.error}) → void
Throws SeoAuditFailure unless report passes at threshold.
auditSeoPages({required List<SeoResolvedPage> pages, required List<SeoRoute> routes, required String siteBase, SeoAuditPolicy policy = const SeoAuditPolicy(), Map<String, Object> resolverFailures = const {}}) SeoAuditReport
Audits an already-resolved set of pages.
auditSeoRoutes({required List<SeoRoute> routes, required String siteBase, List<String> additionalPaths = const [], SeoAuditPolicy policy = const SeoAuditPolicy()}) Future<SeoAuditReport>
Audits a route table for the mistakes the package cannot prevent.

Exceptions / Errors

SeoAuditFailure
Thrown by assertSeoHealthy when a report does not pass.