auditSeoPages function

SeoAuditReport auditSeoPages({
  1. required List<SeoResolvedPage> pages,
  2. required List<SeoRoute> routes,
  3. required String siteBase,
  4. SeoAuditPolicy policy = const SeoAuditPolicy(),
  5. Map<String, Object> resolverFailures = const {},
})

Audits an already-resolved set of pages.

Use this when you have run resolveSeoPages yourself — the prerenderer does, so it can audit the exact snapshot it is about to write rather than resolving the site a second time.

Implementation

SeoAuditReport auditSeoPages({
  required List<SeoResolvedPage> pages,
  required List<SeoRoute> routes,
  required String siteBase,
  SeoAuditPolicy policy = const SeoAuditPolicy(),
  Map<String, Object> resolverFailures = const {},
}) {
  final findings = <SeoFinding>[];
  final partial = resolverFailures.isNotEmpty;
  final base = _stripSlash(siteBase);

  for (final entry in resolverFailures.entries) {
    findings.add(SeoFinding(
      check: SeoCheck.resolverFailed,
      severity: SeoSeverity.error,
      path: entry.key,
      message: 'the resolver threw, so this page has no content at all',
      detail: entry.value.toString(),
    ));
  }

  findings.addAll(_auditRouteTable(routes, policy));

  final indexable = [
    for (final p in pages)
      if (p.isIndexable) p
  ];
  for (final page in pages) {
    findings.addAll(_auditPage(page, routes, base, policy));
  }

  // Cross-page checks need the complete set to be sound. With a page
  // missing, "this title is unique" and "nothing links here" are both
  // unprovable, so they are skipped rather than reported wrongly.
  if (!partial) {
    findings.addAll(_auditAcrossPages(indexable, pages, base, policy, routes));
  }

  if (indexable.isEmpty) {
    findings.add(const SeoFinding(
      check: SeoCheck.sitemapEmpty,
      severity: SeoSeverity.error,
      message: 'no page is indexable — sitemap.xml would ship empty',
    ));
  }

  return SeoAuditReport(
    // Filtered here, once, rather than at each check. Asking every
    // check to remember `policy.isEnabled` is the kind of rule that
    // holds until someone adds the thirty-first — and a suppression
    // that silently does nothing is worse than none, because the team
    // believes they have turned the finding off.
    findings: [
      for (final finding in findings)
        if (policy.isEnabled(finding.check)) finding,
    ],
    pagesAudited: pages.length,
    partial: partial,
  );
}