audit static method

List<SeoAuditResult> audit()

Audit the current page and print issues to console

Implementation

static List<SeoAuditResult> audit() {
  if (!Webify.isInitialized) {
    return [
      const SeoAuditResult(
        severity: AuditSeverity.error,
        rule: 'initialization',
        message: 'Webify is not initialized. Call Webify.initialize() first.',
      ),
    ];
  }

  final results = <SeoAuditResult>[];
  final webify = Webify.instance;
  final meta = webify.currentMeta;

  // Check title
  if (webify.currentTitle == null || webify.currentTitle!.isEmpty) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.error,
        rule: 'title-missing',
        message: 'Page title is missing. Every page must have a <title>.',
        fix: 'Add title parameter to SeoHead or SeoPage widget.',
      ),
    );
  } else if (webify.currentTitle!.length > 60) {
    results.add(
      SeoAuditResult(
        severity: AuditSeverity.warning,
        rule: 'title-too-long',
        message:
            'Title is ${webify.currentTitle!.length} chars (recommended: ≤60).',
        fix: 'Shorten your title for better display in search results.',
      ),
    );
  }

  // Check description
  final description = meta['description'];
  if (description == null || description.isEmpty) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.error,
        rule: 'description-missing',
        message: 'Meta description is missing.',
        fix: 'Add description parameter to SeoHead or SeoPage widget.',
      ),
    );
  } else if (description.length > 160) {
    results.add(
      SeoAuditResult(
        severity: AuditSeverity.warning,
        rule: 'description-too-long',
        message:
            'Description is ${description.length} chars (recommended: ≤160).',
        fix: 'Shorten your description. Google truncates after ~160 chars.',
      ),
    );
  } else if (description.length < 50) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.info,
        rule: 'description-too-short',
        message: 'Description is very short (recommended: 50-160 chars).',
        fix:
            'Write a more detailed description for better click-through rates.',
      ),
    );
  }

  // Check OG tags
  if (!meta.containsKey('og:title')) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.warning,
        rule: 'og-title-missing',
        message: 'og:title is missing. Social shares won\'t show a title.',
        fix: 'SeoHead automatically sets og:title from the title parameter.',
      ),
    );
  }

  if (!meta.containsKey('og:image')) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.warning,
        rule: 'og-image-missing',
        message:
            'og:image is missing. Social shares won\'t show a preview image.',
        fix: 'Add ogImage parameter or ogTags with image to SeoHead.',
      ),
    );
  }

  // Check Twitter card
  if (!meta.containsKey('twitter:card')) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.info,
        rule: 'twitter-card-missing',
        message: 'Twitter card meta is missing.',
        fix: 'Add twitterCard parameter to SeoHead for Twitter/X previews.',
      ),
    );
  }

  // Check canonical
  // Note: We can't easily read canonical from our tracking,
  // but we can check if it was set through meta
  if (!meta.containsKey('og:url')) {
    results.add(
      const SeoAuditResult(
        severity: AuditSeverity.info,
        rule: 'canonical-missing',
        message: 'Canonical URL may not be set.',
        fix: 'Add canonicalUrl parameter to SeoHead or SeoPage.',
      ),
    );
  }

  // Renderer warning
  if (webify.platform.isWeb) {
    final renderer = webify.platform.rendererType;
    if (renderer == RendererType.canvasKit) {
      results.add(
        const SeoAuditResult(
          severity: AuditSeverity.info,
          rule: 'canvaskit-renderer',
          message:
              'CanvasKit renderer detected. All visual content is invisible '
              'to crawlers. Meta tags and JSON-LD from webify are your '
              'only SEO signals.',
        ),
      );
    }
  }

  // Print results in debug mode
  if (Webify.instance.config.debugMode) {
    _printResults(results);
  }

  return results;
}